package com.gallop.mq.core;

import com.gallop.mq.core.exception.MQException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.ToString;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Collection;
import java.util.List;
import java.util.function.Function;

@FunctionalInterface
public interface MQReliableRegistry {

    List<ReliableDeliveryConfig> register();

    @Data
//    @Builder
    @AllArgsConstructor
//    @NoArgsConstructor
    class ReliableDeliveryConfig {

        private String topic;
        private Collection<String> listeners;
        private Class<?> messageType;
        private ReliableMessageIdProvider idProvider;
        private ReliableDeliveryHandler beforeSend;
        private ReliableDeliveryHandler beforeAck;
        private ReliableDeliveryHandler afterAck;

        public static Builder builder() {
            return new Builder();
        }

        @ToString
        public static class Builder {
            private String topic;
            private Collection<String> listeners;
            private Class<?> messageType;
            private ReliableMessageIdProvider idProvider;
            private ReliableDeliveryHandler beforeSend;
            private ReliableDeliveryHandler beforeAck;
            private ReliableDeliveryHandler afterAck;

            public Builder topic(String topic) {
                this.topic = topic;
                return this;
            }

            public Builder listeners(Collection<String> listeners) {
                this.listeners = listeners;
                return this;
            }

            public <R> Builder idProvider(Class<R> messageType, Function<R, String> idProvider) {
                this.messageType = messageType;
                this.idProvider = new ReliableMessageIdProvider() {
                    @Override
                    public <T> String extract(T message) {
                        return idProvider.apply((R) message);
                    }
                };
                return this;
            }

            public Builder beforeSend(ReliableDeliveryHandler beforeSend) {
                this.beforeSend = beforeSend;
                return this;
            }

            public Builder beforeAck(ReliableDeliveryHandler beforeAck) {
                this.beforeAck = beforeAck;
                return this;
            }

            public Builder afterAck(ReliableDeliveryHandler afterAck) {
                this.afterAck = afterAck;
                return this;
            }

            public ReliableDeliveryConfig build() {
                if (this.beforeSend == null) {
                    this.beforeSend = (cfg, topic, message, group) -> true;
                }
                if (this.beforeAck == null) {
                    this.beforeAck = (cfg, topic, message, group) -> true;
                }
                if (this.afterAck == null) {
                    this.afterAck = (cfg, topic, message, group) -> true;
                }
                if (!StringUtils.hasLength(topic) || messageType == null || idProvider == null || CollectionUtils.isEmpty(listeners)) {
                    throw new MQException("ReliableDeliveryConfig cant be built due to uncompleted builder message.builder=" + this);
                }
                return new ReliableDeliveryConfig(topic, listeners, messageType, idProvider, beforeSend, beforeAck, afterAck);
            }
        }
    }

    @FunctionalInterface
    interface ReliableDeliveryHandler {
        boolean apply(ReliableDeliveryConfig cfg, String topic, Object message, String group);
    }

    @FunctionalInterface
    interface ReliableListenerProvider {

        Collection<String> provide(String topic);
    }

    @FunctionalInterface
    interface ReliableMessageIdProvider {

        <T> String extract(T message);
    }
}
